package bingo.web.mvc;

import java.io.BufferedReader;
import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.bingosoft.common.ObjectRegistry;
import net.bingosoft.common.data.ValueBean;
import net.bingosoft.common.struts.ActionContext;
import net.bingosoft.common.util.ClassUtil;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.MethodBeforeAdvice;

import bingo.common.annotation.Autowire;
import bingo.common.annotation.Param;
import bingo.common.annotation.ParamBean;
import bingo.common.annotation.ParamMap;
import bingo.common.utils.CacheabledParameterNameDiscover;
import bingo.web.mvc.exception.IExceptionHandler;
import bingo.web.mvc.plugin.Plugin;
import bingo.web.mvc.plugin.PluginManager;
import bingo.web.mvc.plugin.SystemInitPlugin;


/**
 * Jan 21, 2009 11:12:25 AM
 * Author:yangdongyu(yangdy@bingosoft.net)
 * ReadMe:请求分发器，负责将请求分发到对应的处理器
 * ChangeLog:
 */
public class RequestDispatherServlet extends HttpServlet{

	private static final long serialVersionUID = 1L;
	private static final Log log = LogFactory.getLog(RequestDispatherServlet.class);
	private IExceptionHandler exceptionHandler;



	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
	throws ServletException, IOException {
		doProcess(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
	throws ServletException, IOException {
		doProcess(req, resp);
	}
	/**
	 * 没有找到对应ActionConfig时的处理函数
	 * @param request
	 * @param response
	 * @param path
	 * @return
	 * @throws IOException 
	 */
	protected boolean doProcess(HttpServletRequest request,
			HttpServletResponse response) throws IOException{
		boolean isProcessed = false;
		String path = request.getServletPath();
		path = path.substring(1, path.lastIndexOf('.'));
		String []requestURIInfo = path.split("/");
		int length = requestURIInfo.length;
		String beanIdOrSmartActionName = requestURIInfo[length-1];
		if(beanIdOrSmartActionName.contains(".")){
			int dotIndex = beanIdOrSmartActionName.lastIndexOf('.');
			String beanId = beanIdOrSmartActionName.substring(0,dotIndex);
			String methodName = beanIdOrSmartActionName.substring(dotIndex+1);
			//spring 优先
			Object smartAction = ObjectRegistry.containsBean(beanId) ? ObjectRegistry.getBean(beanId) : null;
			
			if( smartAction != null){
				try {
					ActionContext.init(request, response);
					callService(smartAction,methodName);    				
					isProcessed = true;
				}  catch (Exception exeception) {
					isProcessed = true;
					log.warn("call ["+smartAction.getClass().getSimpleName()+"] method [" +methodName+"] failed ...  ",exeception);
					try {
						processException(request, response,exeception);
					}catch (Exception se) {
						throw new RuntimeException("exeception happend while processing error ...",se);
					}
				}finally{
					ActionContext.destory();
				}
			}else{
				throw new RuntimeException("No bean with id [" + beanId + "] is found,please check") ;
			}
		}
		return isProcessed;
	}

	protected void processException(HttpServletRequest request,
			HttpServletResponse response, Exception exeception) throws ServletException, IOException {
		if(exceptionHandler!=null){
			exceptionHandler.processException(request, response, exeception);
		}else{
			log.warn("处理请求时出现异常",exeception);
		}

	}

	/**
	 * 调用服务类方法
	 * @param target
	 * @param methodName
	 * @throws Throwable 
	 */
	protected void callService(Object target,String methodName) {
		try {
			List<Method> methods = ClassUtil.getMethods(target.getClass(), methodName);
			if(methods.size() > 1){
				throw new RuntimeException("More than one method with name "+ methodName + " is found,please check ...");
			}else{
				Method method = methods.get(0);
				Class[] parameterTypes = method.getParameterTypes();
				List params = new ArrayList();
				if(parameterTypes.length >0 ){
					Object paramValue;
					HttpServletRequest request = ActionContext.getRequest();
					Annotation[][] annotationArray = method.getParameterAnnotations();
					for ( int index = 0; index < annotationArray.length;index ++) {
						String paramName = null;
						Annotation[] annotations = annotationArray[index];
						paramValue = null;
						for (Annotation annotation : annotations) {
							if (annotation instanceof Param) {
								paramName = ((Param) annotation).value();
								paramValue = ConvertUtils.convert(request.getParameter(paramName), parameterTypes[index]);
							} else if (annotation instanceof ParamBean) {
								if(parameterTypes[index] == ValueBean.class){
									paramValue = new ValueBean(getParameterMap(request));
								}else{
									paramValue = parameterTypes[index].newInstance();
									populate(paramValue, request);
								}

							}else if (annotation instanceof ParamMap) {
								paramValue = getParameterMap(request);
							}else if (annotation instanceof Autowire) {
								paramValue = ObjectRegistry.getBean(((Autowire)annotation).value());
							}
						}
						//处理不带注解的request和response参数
						if(paramValue == null){
							if(parameterTypes[index] == HttpServletRequest.class){
								paramValue = ActionContext.getRequest();
							}else if(parameterTypes[index] == HttpServletResponse.class){
								paramValue = ActionContext.getResponse();
							}else if(parameterTypes[index] == String.class || ClassUtil.isPrimitiveOrWrapper(parameterTypes[index])){
								try{
									paramName = CacheabledParameterNameDiscover.getMethodParameterNames(method)[index];
									//get param from request
									paramValue = ConvertUtils.convert(request.getParameter(paramName), parameterTypes[index]);
								}catch (Throwable e) {
									//ignore ex
								}
							}
						}
						params.add(paramValue);
					}
				}

				Object[] args = params.toArray();

				if(target instanceof MethodBeforeAdvice){
					((MethodBeforeAdvice)target).before(method, args, target);
				}
				method.invoke(target,args );
			}

		} catch (Throwable e) {
			//should wap ?? 
			throw new RuntimeException(e);
		}
	}

	@Override
	public void destroy() {
		super.destroy();
		SystemInitPlugin systemPlugin = new SystemInitPlugin();
		systemPlugin.destory(this);

		PluginManager manager = ObjectRegistry.getBean("pluginManager",PluginManager.class);
		for(Plugin plugin : manager.getPlugins()){
			plugin.destory(this);
		}
	}

	@Override
	public void init() throws ServletException {
		super.init();
		SystemInitPlugin systemPlugin = new SystemInitPlugin();
		systemPlugin.init(this);
		if(ObjectRegistry.containsBean("pluginManager")){
			PluginManager manager = ObjectRegistry.getBean("pluginManager",PluginManager.class);
			for(Plugin plugin : manager.getPlugins()){
				plugin.init(this);
			}
		}


		if(ObjectRegistry.containsBean("exceptionHandler")){
			exceptionHandler = ObjectRegistry.getBean("exceptionHandler",IExceptionHandler.class);
		}
	}
	/**
	 * 获取请求中的参数表
	 * @param request
	 * @return
	 */
	protected Map<String,Object> getParameterMap(HttpServletRequest request){
		Map<String,Object> paramMap = new HashMap<String,Object>();
		String name;
		//get url param
		Enumeration<?> names = request.getParameterNames();
		while (names.hasMoreElements()) {
			name = (String) names.nextElement();
			String[] s = request.getParameterValues(name);
			if (s != null) {
				if (s.length > 1) {
					paramMap.put(name, s);
				}else{
					paramMap.put(name, s[0]);
				}
			}
		}
		//get attribute param
		Enumeration attributeNames = request.getAttributeNames();
		while (names.hasMoreElements()) {
			name = attributeNames.nextElement().toString();
			paramMap.put(name, request.getAttribute(name));
		}
		// 以http body方式提交的参数
		try {
			String charset = request.getCharacterEncoding();
			if (charset == null)
				charset = "UTF-8";
			BufferedReader in = new BufferedReader(new InputStreamReader(request.getInputStream(), charset));
			// Read the request
			CharArrayWriter data = new CharArrayWriter();
			char buf[] = new char[4096];
			int ret;
			while ((ret = in.read(buf, 0, 4096)) != -1)
				data.write(buf, 0, ret);

			// URL解码
			String content = URLDecoder.decode(data.toString().trim(), charset);
			// 组装参数
			if (content != "") {
				String[] paramPairs = content.split("&");
				String[] kv;
				for (String p : paramPairs) {
					kv = p.split("=");
					if (kv.length > 1)
						paramMap.put(kv[0], kv[1]);
				}
			}

			data.close();
			in.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return paramMap;
	}
	/**
	 * 将request中的数据根据参数名称注入给定的bean中
	 * @param bean
	 * @param request
	 * @throws ServletException
	 */
	public void populate(Object bean,HttpServletRequest request)
	throws ServletException {
		// Set the corresponding properties of our bean
		try {
			Map <String,Object>params = getParameterMap(request);
			BeanUtils.populate(bean, params);
		} catch(Exception e) {
			throw new ServletException("BeanUtils.populate", e);
		} 

	}





}
